package com.dongdong.service;

import com.dongdong.controller.utils.ProjectExceptionAdvice;
import com.dongdong.entity.Filepath;
import com.dongdong.mapper.FilepathMapper;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.errors.MinioException;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PrivilegedActionException;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author dongdong 抖音号：W13883155245
 * @version 1.0
 * @date 2025/5/5
 * @description
 */
@Service
public class FileService {

    private static final Logger logger = LoggerFactory.getLogger(FileService.class);

    @Autowired
    private MinioClient minioClient;

    @Autowired
    private FilepathMapper filepathMapper;

    @Value("${spring.minio.bucket-name}")
    private String bucketName;

    /**
     * 上传文件到 MinIO 并保存文件信息到数据库
     * @param file 上传的文件
     * @param userId 用户 ID
     * @throws IOException 文件操作异常
     * @throws MinioException MinIO 操作异常
     * @throws NoSuchAlgorithmException 算法异常
     * @throws InvalidKeyException 密钥异常
     */
    @Transactional
    public void uploadFile(MultipartFile file, Long userId) throws IOException, MinioException, NoSuchAlgorithmException, InvalidKeyException {
        String fileName = file.getOriginalFilename();
        logger.info("开始上传用户 {} 的文件: {}", userId, fileName);
        try (InputStream inputStream = file.getInputStream()) {
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build();
            minioClient.putObject(putObjectArgs);

            Filepath filepath = new Filepath();
            filepath.setUserId(userId);
            filepath.setFilePath(fileName);
            filepath.setFileName(fileName);

            filepathMapper.insert(filepath);
            logger.info("用户 {} 的文件: {} 上传成功", userId, fileName);
        } catch (Exception e) {
            logger.error("用户 {} 的文件: {} 上传失败", userId, fileName, e);
            throw new IOException("文件上传失败", e);
        }
    }

    /**
     * 下载用户的文件
     * @param request HttpServletRequest 对象
     * @param response HttpServletResponse 对象
     * @param userId 用户 ID
     * @throws IOException 文件操作异常
     * @throws MinioException MinIO 操作异常
     * @throws InvalidKeyException 密钥异常
     * @throws NoSuchAlgorithmException 算法异常
     */
    public void downloadFiles(HttpServletRequest request, HttpServletResponse response, Long userId) throws IOException, MinioException, InvalidKeyException, NoSuchAlgorithmException {
        logger.info("开始下载用户 {} 的文件", userId);
        // 获取用户对应的所有文件路径
        List<Filepath> filepaths = filepathMapper.selectByUserId(userId);
        if (filepaths.isEmpty()) {
            logger.warn("用户 {} 没有上传任何文件", userId);
            throw new IllegalArgumentException("该用户没有上传任何文件");
        }

        // 假设只下载第一个文件，你可以根据需求调整逻辑
        Filepath firstFilepath = filepaths.get(0);
        // 使用 fileName 作为下载文件名
        String objectName = firstFilepath.getFileName();

        // 获取文件后缀名
        String fileExtension = getFileExtension(objectName);
        // 根据文件后缀设置 Content-Type
        String contentType = getContentTypeByExtension(fileExtension);
        response.setContentType(contentType);

        // 根据浏览器类型对文件名进行编码
        String encodedFileName = encodeFileName(request, objectName);

        // 设置响应头，让浏览器识别为下载文件
        response.setHeader("Content-Disposition", "attachment; filename=" + encodedFileName);

        try (InputStream inputStream = minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
             OutputStream outputStream = response.getOutputStream()) {

            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, length);
            }
            outputStream.flush();
            logger.info("用户 {} 的文件 {} 下载完成", userId, objectName);
        } catch (Exception e) {
            logger.error("用户 {} 的文件 {} 下载失败", userId, objectName, e);
            throw new IOException("下载文件时出现错误", e);
        }
    }
    /**
     * 下载用户的文件
     * @param  pileNumber 桩号
     */
    public List<Filepath> getPileImagePath(String pileNumber) {
        logger.info("开始查询用户 {} 的文件", pileNumber);
        // 获取用户对应的所有文件路径
        List<Filepath> filepaths = filepathMapper.selectByPileNumber(pileNumber);
        if (filepaths.isEmpty()) {
            logger.warn("没有找到{}上传任何文件 ", pileNumber);
            //throw new IllegalArgumentException("没有找到"+pileNumber+"上传任何文件");
            throw new RuntimeException("没有找到"+pileNumber+"上传任何文件");

        }
        return filepaths;
    }

    /**
     * 获取文件后缀名
     * @param fileName 文件名
     * @return 文件后缀名
     */
    private String getFileExtension(String fileName) {
        int lastIndex = fileName.lastIndexOf(".");
        if (lastIndex != -1 && lastIndex < fileName.length() - 1) {
            return fileName.substring(lastIndex + 1);
        }
        return "";
    }

    /**
     * 根据文件后缀返回 Content-Type
     * @param extension 文件后缀
     * @return Content-Type
     */
    private String getContentTypeByExtension(String extension) {
        switch (extension.toLowerCase()) {
            case "pdf":
                return "application/pdf";
            case "jpg":
            case "jpeg":
                return "image/jpeg";
            case "png":
                return "image/png";
            case "txt":
                return "text/plain";
            case "docx":
                return "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "pptx":
                return "application/vnd.openxmlformats-officedocument.presentationml.presentation";
            default:
                return "application/octet-stream";
        }
    }

    /**
     * 根据浏览器类型对文件名进行编码
     * @param request HttpServletRequest 对象
     * @param fileName 文件名
     * @return 编码后的文件名
     * @throws IOException 编码异常
     */
    private String encodeFileName(HttpServletRequest request, String fileName) throws IOException {
        String userAgent = request.getHeader("User-Agent").toLowerCase();
        if (userAgent.contains("msie") || userAgent.contains("trident") || userAgent.contains("edge")) {
            // IE 浏览器和 Edge 浏览器
            return URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        } else if (userAgent.contains("chrome")) {
            // Chrome 浏览器
            return new String(fileName.getBytes(StandardCharsets.UTF_8), "ISO-8859-1");
        } else if (userAgent.contains("safari")) {
            // Safari 浏览器
            return URLEncoder.encode(fileName, "UTF-8");
        } else if (userAgent.contains("firefox")) {
            // Firefox 浏览器
            return "=?UTF-8?B?" + java.util.Base64.getEncoder().encodeToString(fileName.getBytes(StandardCharsets.UTF_8)) + "?=";
        } else {
            // 其他浏览器
            return URLEncoder.encode(fileName, "UTF-8");
        }
    }

    /**
     *
     * @param file 要上传的文件
     * @param userId 谁上传的
     * @param pileNumber 给那个桩上传的
     * @param operation 为那个工序上传的
     * @throws IOException
     */
    @Transactional(rollbackFor = Exception.class)
    public void uploadFile(MultipartFile file, Long userId, String pileNumber, String operation) throws IOException {

        String fileName = file.getOriginalFilename();
        String path = pileNumber + "/" + fileName;
        logger.info("开始上传用户 {} 的文件: {},路径{}", userId, fileName,path);
        try (InputStream inputStream = file.getInputStream()) {
            //上传到minio
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(bucketName) //桶名
                    .object(path) //路径对象
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build();
            minioClient.putObject(putObjectArgs);

            //保存到数据库
            Filepath filepath = new Filepath();
            filepath.setUserId(userId);
            filepath.setFilePath(path);
            filepath.setFileName(fileName);
            filepath.setPileNumber(pileNumber);
            filepath.setOperation(operation);
            filepath.setFileType(file.getContentType());
            filepathMapper.insert(filepath);
            logger.info("用户 {} 的文件: {} 上传成功 {}", userId, fileName,file.getContentType());
        } catch (Exception e) {
            logger.error("用户 {} 的文件: {} 上传失败", userId, fileName, e);
            throw new IOException("文件上传失败", e);
        }
    }

    /**
     * 删除用户的文件（从 MinIO 和数据库中）
     * @param fileId 文件ID
     * @param userId 用户ID（用于权限验证）
     * @throws IOException 文件操作异常
     * @throws MinioException MinIO 操作异常
     * @throws InvalidKeyException 密钥异常
     * @throws NoSuchAlgorithmException 算法异常
     */
    @Transactional
    public boolean deleteFile(Long fileId, Long userId) throws IOException, MinioException, InvalidKeyException, NoSuchAlgorithmException {
        logger.info("开始删除用户 {} 的文件，文件ID: {}", userId, fileId);

        // 查询文件信息
        Filepath filepath = filepathMapper.selectById(fileId);
        if (filepath == null) {
            logger.warn("文件ID {} 不存在", fileId);
            throw new IllegalArgumentException("文件不存在");
        }

        // 验证文件所属用户
        if (!filepath.getUserId().equals(userId)) {
            logger.warn("用户 {} 尝试删除不属于自己的文件 {}", userId, fileId);
            throw new SecurityException("无权删除此文件");
        }

        try {
            // 从 MinIO 删除文件
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(bucketName)
                            .object(filepath.getFilePath())
                            .build()
            );

            // 从数据库删除记录
            filepathMapper.deleteById(fileId);

            logger.info("用户 {} 的文件 {} 删除成功", userId, filepath.getFileName());
            return true;
        } catch (Exception e) {
            logger.error("用户 {} 的文件 {} 删除失败", userId, filepath.getFileName(), e);
            throw new IOException("文件删除失败", e);
        }

    }

    /**
     * 删除用户的文件（从 MinIO 和数据库中）
     * @param pileNumber 文件ID
     * @param userId 用户ID（用于权限验证）
     * @throws IOException 文件操作异常
     * @throws MinioException MinIO 操作异常
     * @throws InvalidKeyException 密钥异常
     * @throws NoSuchAlgorithmException 算法异常
     */
    @Transactional
    public void deleteFileByPileNumber(String pileNumber, Long userId) throws IOException, MinioException, InvalidKeyException, NoSuchAlgorithmException {
        logger.info("开始删除用户 {} 的文件，桩号pileNumber: {}", userId, pileNumber);

        // 查询文件信息
       List<Filepath>  filepaths = filepathMapper.selectByPileNumberAndOperation(pileNumber,"资料数据");

        if (filepaths.size()<=0) {
            logger.warn("文件ID {} 不存在", pileNumber);
            throw new IllegalArgumentException("文件不存在");
        }

        for (Filepath filepath : filepaths) {
            // 验证文件所属用户
            if (!filepath.getUserId().equals(userId)) {
                logger.warn("用户 {} 尝试删除不属于自己的文件 {}", userId, pileNumber);
                throw new SecurityException("无权删除此文件");
            }

            try {
                // 从 MinIO 删除文件
                minioClient.removeObject(
                        RemoveObjectArgs.builder()
                                .bucket(bucketName)
                                .object(filepath.getFilePath())
                                .build()
                );

                // 从数据库删除记录
                filepathMapper.deleteById(filepath.getId());

                logger.info("用户 {} 的文件 {} 删除成功", userId, filepath.getFileName());
            } catch (Exception e) {
                logger.error("用户 {} 的文件 {} 删除失败", userId, filepath.getFileName(), e);
                throw new IOException("文件删除失败", e);
            }
        }

    }
}
















